
#include <string.h>

#include "hy_hh_codec.h"


static const unsigned int EncodeMiniValue = 0x3C3C3C3C;

#define DWORD_SIZE sizeof(unsigned int)

int sbuffer_sperate( const char* head, const char* tail, const char* src, int src_len, int offset, char*& dest )
{
	char* dest_head = 0,* dest_tail = 0,* delimiter = 0;
	if ((delimiter = (char*)strstr(src, tail)) == 0 || delimiter - src > src_len)
	{
		return -1;
	}

	dest_tail = delimiter;
	char* repeat = (char*)src;

	while ((delimiter = strstr(repeat, head)) != 0)
	{
		if (dest_tail - delimiter < offset)
		{
			break;
		}
		(repeat = dest_head = delimiter)++;
	}

	if ((dest = dest_head) == 0)
	{
		return -1;
	}
	return (int)(dest_tail - dest_head + 1);
}

int encode_24bit_buf( unsigned int* src, int src_unit, unsigned int* dest, int dest_unit )
{
	static const unsigned int FrontMasks[3] =
	{
		0x00000000, 0xC0C0C0C0, 0xF0F0F0F0
	};
	static const unsigned int BeHindMasks[3] =
	{
		0x3F3F3F3F, 0x0F0F0F0F, 0x03030303
	};
	static const unsigned int ExhaustMasks[3] =
	{
		0xC0C0C0C0, 0x30303030, 0x0C0C0C0C
	};

	unsigned int dwIdx, dwFront, dwBehind, dwExhaust, dwNewCode;
	int result = 0;
	dwIdx = dwNewCode = dwExhaust = 0;
	while (src_unit > 0)
	{
		if (dest_unit <= 0)
			break;
		dwFront = *src & FrontMasks[dwIdx];
		dwBehind = *src & BeHindMasks[dwIdx];
		dwExhaust = *src & ExhaustMasks[dwIdx];
		if (dwIdx == 0)
		{
			*dest = dwBehind + EncodeMiniValue;
			dwNewCode = dwExhaust;
			dwIdx = 1;
		}
		else if (dwIdx == 1)
		{
			*dest = ((dwFront >> 2) | (dwBehind)) + EncodeMiniValue;
			dwNewCode = dwNewCode | dwExhaust;
			dwIdx = 2;
		}
		else
		{
			*dest = ((dwFront >> 2) | (dwBehind)) + EncodeMiniValue;
			result++;
			dest++;
			dest_unit--;

			if (!dest_unit)
				break;

			*dest = ((dwNewCode | dwExhaust) >> 2) + EncodeMiniValue;
			dwIdx = 0;
		}
		result++;
		src_unit--;
		dest_unit--;
		src++;
		dest++;
	}
	if (dwIdx && dest_unit > 0)
	{
		*dest = ((dwNewCode | dwExhaust) >> 2) + EncodeMiniValue;
		result++;
	}
	return result;
}

int decode_24bit_buf( unsigned int* src, int src_unit, unsigned int* dest, int dest_unit )
{
	static const unsigned int FrontMasks[3] =
	{
		0x00000000, 0x30303030, 0x3C3C3C3C
	};
	static const unsigned int BeHindMasks[3] =
	{
		0x3F3F3F3F, 0x0F0F0F0F, 0x03030303
	};
	static const unsigned int IncludeMasks[3] =
	{
		0xC0C0C0C0, 0x30303030, 0x0C0C0C0C
	};

	unsigned int dwIdx, dwFront, dwBehined, dwInclude, dwThisCode;
	int result = 0;
	if (src_unit < 1)
		return result;
	dwIdx = dwInclude = 0;
	while (src_unit > 0)
	{
		if (dest_unit <= 0)
			break;
		dwThisCode = *src - EncodeMiniValue;
		if (dwIdx == 0)
		{
			dwInclude = src_unit > 3 ? src[3] : src[src_unit - 1];
			dwInclude = (dwInclude - EncodeMiniValue) << 2;
			src_unit--;

			*dest = dwThisCode | (dwInclude & IncludeMasks[0]);
			dwIdx = 1;
		}
		else if (dwIdx == 1)
		{
			dwFront = (dwThisCode & FrontMasks[1]) << 2;
			dwBehined = dwThisCode & BeHindMasks[1];

			*dest = dwFront | dwBehined | (dwInclude & IncludeMasks[1]);

			dwIdx = 2;
		}
		else if (dwIdx == 2)
		{
			dwFront = (dwThisCode & FrontMasks[2]) << 2;
			dwBehined = dwThisCode & BeHindMasks[2];

			*dest = dwFront | dwBehined | (dwInclude & IncludeMasks[2]);
			dwIdx = 0;

			src++;
		}
		result++;
		src++;
		dest++;
		src_unit--;
		dest_unit--;
	}
	return result;
}

int calc_source_unit( int size )
{
	return (size % DWORD_SIZE) ? (size / DWORD_SIZE + 1) : (size / DWORD_SIZE);
}

int calc_encode_unit( int de_unit )
{
	int en_unit = de_unit / 3 * 4;
	int rm_unit = de_unit % 3;
	if (rm_unit > 0)
		en_unit += rm_unit + 1;
	return en_unit;
}

int calc_decode_unit( int en_unit )
{
	int rm_unit = en_unit & 3;
	int de_unit = en_unit >> 2;
	de_unit += de_unit << 1;
	return (rm_unit > 0) ? (de_unit + rm_unit - 1) : de_unit;
}

int calc_encode_size( int de_size )
{
	return calc_encode_unit(calc_source_unit(de_size)) * DWORD_SIZE;
}

int calc_decode_size( int en_size )
{
	return calc_decode_unit(calc_source_unit(en_size)) * DWORD_SIZE;
}

int sbuffer_encode( const char* src, int src_len, char* dest, int dest_len )
{
	if (src == 0 || dest == 0 || calc_encode_size(src_len) > dest_len)
	{
		return -1;
	}
	int src_unit = calc_source_unit(src_len);
	return encode_24bit_buf((unsigned int *)src, src_unit, (unsigned int *)dest, calc_encode_unit(src_unit)) * DWORD_SIZE;
}

int sbuffer_decode( const char* src, int src_len, char* dest, int dest_len )
{
	if (src == 0 || dest == 0 || calc_decode_size(src_len) > dest_len)
	{
		return -1;
	}
	int src_unit = calc_source_unit(src_len);
	return decode_24bit_buf((unsigned int *)src, src_unit, (unsigned int *)dest, calc_decode_unit(src_unit)) * DWORD_SIZE;
}
